package main

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"github.com/peterh/liner"
	log "github.com/sirupsen/logrus"
	"mytinydb/utils"
	"net"
	"os"
	"strings"
)

var commandList = [][]string{
	{"SET", "key value", "STRING"},
	{"GET", "key value", "STRING"},
	{"SETNX", "key value", "STRING"},
	{"EXISTS", "key value", "STRING"},
	{"GETSET", "key value", "STRING"},
	{"APPEND", "key value", "STRING"},
	{"STRLEN", "key value", "STRING"},
}

// 历史命令记录地址
var (
	cmdHistoryPath = utils.CmdHistoryPath
	host           = utils.Addr
	port           = utils.Port
)

func main() {
	addr := fmt.Sprintf("%s:%d", host, port)
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		log.Fatal("err", err)
	}
	line := liner.NewLiner()
	defer line.Close()

	// 设置 ctrl+c 退出
	line.SetCtrlCAborts(true)
	line.SetCompleter(func(li string) (res []string) {
		for _, c := range commandList {
			if strings.HasPrefix(c[0], strings.ToUpper(li)) {
				res = append(res, strings.ToLower(c[0]))
			}
		}
		return
	})

	// 打开和保存历史命令
	if f, err := os.Open(cmdHistoryPath); err == nil {
		line.ReadHistory(f)
		f.Close()
	}
	defer func() {
		if f, err := os.Create(cmdHistoryPath); err != nil {
			log.Warn("写入日志文件发生错误:", err)
		} else {
			line.WriteHistory(f)
			f.Close()
		}
	}()

	// 初始化有哪些命令
	haveCommand := map[string]bool{}
	for _, cmd := range commandList {
		haveCommand[strings.ToLower(cmd[0])] = true
	}

	prompt := addr + ">"
	for {
		cmd, err := line.Prompt(prompt)
		if err != nil {
			log.Println(err)
			break
		}
		// 删除cmd首尾连续的的空白字符
		cmd = strings.TrimSpace(cmd)
		if len(cmd) == 0 {
			continue
		}
		cmd = strings.ToLower(cmd)
		if cmd == "help" {
			//TODO
		} else if cmd == "quit" {
			break
		} else {
			// 按照空格进行分割
			cmdList := strings.Split(cmd, " ")
			if cmdList[0] == "help" && len(cmdList) == 2 {
				// TODO
			} else {
				line.AppendHistory(cmd)
				if haveCommand[cmdList[0]] != true {
					log.Warn("该命令未找到")
					continue
				}
				err := writeRequest(conn, cmd)
				//infoToServer := GetCmdInfo(cmd)
				//_, err := conn.Write(infoToServer)
				if err != nil {
					log.Println(err)
				}
				// 从服务器读取返回值
				reply := readReply(conn)
				fmt.Println(reply)
			}
		}

	}
}

// 将输入的命令编码为二进制数组，传递给服务器 ,首先前4个字节是命令的长度，后面的字节是命令
func writeRequest(conn net.Conn, cmd string) error {
	b := make([]byte, len(cmd)+4)
	binary.BigEndian.PutUint32(b[:4], uint32(len(cmd)))
	copy(b[4:], cmd)

	_, err := conn.Write(b)
	return err
}

func readReply(conn net.Conn) string {
	reader := bufio.NewReader(conn)
	var res string
	b := make([]byte, 4)
	_, err := reader.Read(b)
	if err != nil {
		return ""
	}
	size := binary.BigEndian.Uint32(b)
	if size > 0 {
		data := make([]byte, size)
		reader.Read(data)
		res = string(data)
	}
	return res
}
